package com.example.myproject.client;

import java.util.LinkedList;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.Dictionary;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

public class Modules {
	
	int selectedModule;	
	Module[] moduleArray;	
	Grid g;
	boolean minConfig;
	boolean configChanged;
	
	/*************************************************************************
	 * Function: Modules
	 * @param: none
	 * Purpose: Creates an array of modules
	 * 			Creates a grid to display the list of modules
	 * Return: No return
	 * 
	 * **********************************************************************/
	public Modules()
	{
		//init the array of modules
		moduleArray = new Module[190];
		reset();
		
		//current selected module is num 0
		selectedModule = 0;
		
		//create grid for the module map
		g = new Grid(50, 100);
				
		//get cell formatter for easy use during grid sets
		CellFormatter cf = g.getCellFormatter();		
		//loop through grid and init cells
		for(int i=0; i<g.getRowCount(); i++) 
		{
			for(int j = 0; j<g.getColumnCount(); j++)
			{		
				//set pixel width and height to 13 pixels
				cf.setHeight(i, j, "13px");
				cf.setWidth(i, j, "13px");
			}
		}			
		
		//it def is not a minimum config yet
		minConfig = false;
	}
		
	public void reset()
	{
		for(int i =0; i < moduleArray.length; i++)
			moduleArray[i] = new  Module(i+1);
	}
	
	/*************************************************************************
	 * return the selected module in the list
	 * 
	 * **********************************************************************/	
	public Module getSelectedModule()
	{
		return moduleArray[selectedModule];
	}
		
	/*************************************************************************
	 * sets the selected module
	 * **********************************************************************/
	public void setSelectedModule(int num)
	{
		selectedModule = num;
	}
	
	/*************************************************************************
	 * sets the module number (given by num) with the dictionary provided from 
	 * GPS data 
	 * **********************************************************************/
	public void setModule(int num, Dictionary t1)
	{
		Module mod = moduleArray[num];
		
		//The following are read in from test cases 1 through 5 using a Dictionary
				//Object.
				
		//set modType equal to t1's code string
        mod.setModType(Integer.parseInt(t1.get("code")));
        //set status equal to t1's status string
        mod.setStatus(t1.get("status"));
        //set orient to t1's turn string
        mod.setOrient(Integer.parseInt(t1.get("turns"))); 
        //set locations for x and y on grid
        mod.setX(Integer.parseInt(t1.get("X")));        
        mod.setY(Integer.parseInt(t1.get("Y")));     
        //set isConfiged to true
        mod.setConifigured();     
	}
	
	public void setModule(int num, JSONObject jO)
	{		
		Module mod = moduleArray[num];
		
    	JSONNumber jN;
    	JSONString jS;
    	Double d = 0.0;
    	
		jN = (JSONNumber) jO.get("code");
		d = jN.doubleValue();
		mod.setModType(d.intValue());
		
		jS = (JSONString) jO.get("status");
		String statString = jS.stringValue();
		mod.setStatus(statString);
		
		jN = (JSONNumber) jO.get("turns");
		d = jN.doubleValue();
		mod.setOrient(d.intValue());
				
		jN = (JSONNumber) jO.get("X");
		d = jN.doubleValue();
		mod.setX(d.intValue());	

		jN = (JSONNumber) jO.get("Y");
		d = jN.doubleValue();
		mod.setY(d.intValue());
	
		mod.setConifigured();
	}
	
	/*************************************************************************
	 * populate a given listbox with the module number of each module
	 * **********************************************************************/
	public void popList(ListBox list)
	{
		//for each module in list, add its name
		for(int i =0; i<moduleArray.length; i++)
		{
			list.addItem(String.valueOf(i+1));
		}
	}
	
	/*************************************************************************
	 * getMap will get a viewable map of locations of modules	 * 
	 * **********************************************************************/	
	public Panel getMap()
	{		
		g.clear();
		//create a vertical panel
		VerticalPanel test = new VerticalPanel();
		test.setStyleName("mapStyle");
		//add a grid object to the vertical panel
		test.add(g);
		
		//check each module in the module array
		//if configured, then add that module to the grid at given 
		//positions of locX and locY
		//get and display modules image on the grid
		for(int i=0; i< moduleArray.length; i++)
		{
			if(moduleArray[i].isConfigured())
			{
				final int select = i;
				PushButton b = new PushButton( moduleArray[i].getImage());
				b.setStyleName("clearButton");
				b.addClickHandler(new ClickHandler() {
			        public void onClick(ClickEvent event) {
			        	selectedModule = select;
			        	
			        	final myPopupPanel pop = new myPopupPanel();
			        	
						pop.add(getSelectedModuleInfo());
			
						pop.setPopupPosition(50, 50);
						pop.show();
			        }
			    });		
				g.setWidget(49 - (moduleArray[i].getY()-1), moduleArray[i].getX()-1, b);				
			}
		}
		
		//return the panel with the grid that is the map
		return test;
	}
	
	/*************************************************************************
	 * check the modules and see if a minimum config has ben reached, if it has
	 * minConfig is set to true
	 * **********************************************************************/
	void checkMinConfig()
	{
		//if no minconfig has been foound, set min config rules to false
		if(!minConfig)
		{
			boolean has1air = false, has1con = false, has1pow = false,
					has1food = false, has1dorm = false, has1cant = false,
					has1san = false, has3plain = false;
			
			//create and instantiate a plainCount variable to 0
			//to be used to determine number of plains for min config
			int plainCount = 0;
			
			//search list of modules, if each module is found (given by current
			//minimum configuration restrictions) set it's value to true
			for(int i = 0; i<moduleArray.length; i++)
			{
				if(moduleArray[i].getModType().equals("Airlock"))
						has1air = true;
				else if(moduleArray[i].getModType().equals("Control"))
						has1con = true;
				else if(moduleArray[i].getModType().equals("Power"))
						has1pow = true;
				else if(moduleArray[i].getModType().equals("Food and Water Storage"))
						has1food = true;
				else if(moduleArray[i].getModType().equals("Dormitory"))
						has1dorm = true;
				else if(moduleArray[i].getModType().equals("Canteen"))
					has1cant = true;
				else if(moduleArray[i].getModType().equals("Sanitation"))
					has1san = true;
				else if(moduleArray[i].getModType().equals("Plain"))
				{
					//make sure 3 plains are found before setting it's
					//value to true
					plainCount++;
					if(plainCount > 2)
						has3plain = true;
				}			
			}
			//if minimum config is found(given by current restrictions)
			//set minConfig to true and create display panel			
			if(has1air && has1con && has1pow && has1food &&
					has1dorm && has1cant && has1san && has3plain)
			{
				//set minConfig to true
				minConfig = true;
				//create Popup panel
				final PopupPanel pop = new PopupPanel();
				//create Dock panel
				DockPanel contents = new DockPanel();
				contents.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
				contents.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);
				
				//create a label to display a minimum configuratoin has been met
				Label lab = new Label("You have reached a minimum configuration!");
				
				//create a button to close the popup
				Button okButton = new Button("OK");
				okButton.addClickHandler(new ClickHandler() {
			        public void onClick(ClickEvent event) {
			        	Voices.playMinimumConfig();      
			        	pop.hide();	
			        }
			    });		
				
				//add the label and button to the dock panel
				contents.add(lab, DockPanel.NORTH);
				contents.add(okButton, DockPanel.NORTH);
				//add the dock panel to the popup
				pop.add(contents);
	
				pop.setPopupPosition(302, 201);
				pop.show();
			}
		}
	}
	
	//there are variable that only get used in getting the config panel
	//curPanel is the panel currently being displayed ot the user, and cur 
	//is that modules position in the array of possible panels
	Panel curPanel;
	int cur;	
	public Panel getConfigPanel()
	{
		//create an array of panels with the possible configs
		final Panel[] layouts = getConfigs();
		
		//set the currentl panel diplayed to the first panel in the array
		cur = 0;
		curPanel = new ScrollPanel();
		curPanel.add(layouts[cur]);	
		
		//create a main panle to add options to move around from config to config
		final DockPanel mainPanel = new DockPanel();
		mainPanel.setHorizontalAlignment(DockPanel.ALIGN_CENTER);
		mainPanel.setVerticalAlignment(DockPanel.ALIGN_MIDDLE);		


			//create a panel that will hold buttons for moving around
			DockPanel changePanel = new DockPanel();
			changePanel.setHorizontalAlignment(DockPanel.ALIGN_CENTER);
			changePanel.setVerticalAlignment(DockPanel.ALIGN_MIDDLE);
				//back\button with set the currently displayed config to the previous config
				Button backButton = new Button("Previous Configuration");
				backButton.addClickHandler(new ClickHandler() {
			        public void onClick(ClickEvent event) {
			        	cur--;
			        	//if we cant go any farther back, dont change cur
			        	if(cur<0)
			        		cur = 0;
			        	else
			        	{
			        		curPanel.clear();
			        		curPanel.add(layouts[cur]);
			        	}
			        }
			    });
				//create a button to set current config as the config we wish to use
				Button okButton = new Button("Set Configuration");
				okButton.addClickHandler(new ClickHandler() {
			        public void onClick(ClickEvent event) {			        	
			        	//set all the modules in the current grid to their respective locations
			        	grids[cur].setAll();
			        	
			        	//create a popup that will allow you to undo the changes made
			        	final PopupPanel pop = new PopupPanel();
			        	pop.setPopupPosition(15, 563);			        	
			        	//ad a button to do it allow you to undo the changes made
			        	Button but = new Button("Undo Last Configuration");
			        	but.addClickHandler(new ClickHandler() {
					        public void onClick(ClickEvent event) {	
					        	//when clicked, reset all modules to their previous locations
					        	resetAll();
					        	//hide the popup
					        	pop.hide();
					        }
			        	});
			        	//show the popup
			        	pop.add(but);
			        	pop.show();
			        }
			    });
				//create a button that allows you to move to the next configuration panel
				Button nextButton = new Button("Next Configuration");
				nextButton.addClickHandler(new ClickHandler() {
			        public void onClick(ClickEvent event) {
			        	cur++;
			        	//if there is no more panels to display, don't change cur
			        	if(cur>layouts.length-1)
			        		cur = layouts.length-1;			        	
			        	else
			        	{
			        		curPanel.clear();
			        		curPanel.add(layouts[cur]);
			        	}
			        }
			    });	
				
				//add all the butons to change panel
			changePanel.add(backButton, DockPanel.WEST);
			changePanel.add(okButton, DockPanel.WEST);
			changePanel.add(nextButton, DockPanel.WEST);
		
		//add the current panel to the main panel
		mainPanel.add(curPanel, DockPanel.NORTH);
		//add the panel with buttons to the main panel
		mainPanel.add(changePanel, DockPanel.NORTH);		
		
		return mainPanel;
	}
	
	private void resetAll()
	{
		//loop through all modules and undo their moves if they have been moved
		for(int i = 0; i<moduleArray.length; i++)
			moduleArray[i].undoTempXY();
	}
	
	//grids will be used only in get configs
	myGrid[] grids;
	//getConfigs will get all configurations for a set of modules
	private Panel[] getConfigs()
	{		
		//crete an array of panels to hold all the confis
		Panel[] panels = new ScrollPanel[3];
		for(int i =0; i < panels.length; i++)
		{
			panels[i] = new ScrollPanel();
			panels[i].setSize("700px", "500px");
		}
	
		//only do this if their is a minimum configuration reached
		if(minConfig)
		{			
			//Create an list of only available modules
			LinkedList<Module> avail = new LinkedList<Module>();
			for(int i=0; i< moduleArray.length; i++)
			{
				if(moduleArray[i].isConfigured() && moduleArray[i].checkStatus())
				{
					avail.add(moduleArray[i]);
				}
			}
			
			//create grids of cells for each panel
			grids = new myGrid[3];
			grids[0] = new gridLayout1();		
			grids[1] = new gridLayout2();		
			grids[2] = new gridLayout3();					
			
			//place all plain modules
			for(int i = 0; i < avail.size(); i++)
			{
				if(avail.get(i).getModType().equals("Plain"))
				{
					Module temp = avail.remove(i);
					grids[0].add(temp);
					grids[1].add(temp);
					grids[2].add(temp);
					i--;
				}
			}
			
			//check each layout, and make sure that all plains are placed
			grids[0].checkLayout();
			grids[1].checkLayout();
			grids[2].checkLayout();
			
			//attempt to place all modules that havent been placed yet
			while(!avail.isEmpty())
			{
					Module temp = avail.remove();
					grids[0].add(temp);
					grids[1].add(temp);
					grids[2].add(temp);
			}			

			//place any orphaned modules into any locaiton they can fit
			grids[0].fillEmpty();
			grids[1].fillEmpty();
			grids[2].fillEmpty();
			
			//add the respected grids to the respected panels
			panels[0].add(grids[0].toPan());
			panels[1].add(grids[1].toPan());
			panels[2].add(grids[2].toPan());
		}
		
		return panels;
	}
	
	//gets a panel that displays info for the selected module
	public DockPanel getSelectedModuleInfo()
	{				
		final Module mod = moduleArray[selectedModule];
		
		final DockPanel info = new DockPanel();		
		info.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
		info.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);
		
		//create a new DockPanel
		DockPanel panel1 = new DockPanel();
		//set size and alignment of new panel
		panel1.setSize("640px", "50px");
		panel1.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
		panel1.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);	
		
		//create a new Label
		Label modNumLabel = new Label("Module Number");		
		//set size of modNumLabel
		modNumLabel.setSize("300px", "50px");
		//set modNumText to the string vaue of monNum
		final Label modNumText = new Label(String.valueOf(mod.getModNum()) );		
		//set size of modNumText
		modNumText.setSize("300px", "50px");		
		//add the label and text to the panel
		panel1.add(modNumLabel, DockPanel.WEST);
		panel1.add(modNumText, DockPanel.WEST);
		
		//add the panel to the DockPanel
		info.add(panel1, DockPanel.NORTH);

		//create a second DockPanel
		DockPanel panel2 = new DockPanel();		
		//set panel size and alignment
		panel2.setSize("640px", "50px");
		panel2.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
		panel2.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);
		
		//create a label 
		Label modTypeLabel = new Label("Module Type");		
		//set size of new label
		modTypeLabel.setSize("300px", "50px");		
		//set modTypeText to Label modType
		
		final Label modTypeText = new Label(mod.getModType());		
		//create a click event for a popup panel
		modTypeText.addClickHandler(new ClickHandler() {
	        public void onClick(ClickEvent event) {
				final PopupPanel pop = new PopupPanel();
				pop.setPopupPosition(302, 201);
				
				//create 2 new DockPanels
				DockPanel contents = new DockPanel();
				DockPanel panel1 = new DockPanel();
				
				//add the modtype label to panel1
				panel1.add(new Label("Modual Type"),DockPanel.WEST);
				
				//create a ListBox to store modtypes
				final ListBox list = new ListBox();
				list.addItem("Plain");
				list.addItem("Dormitory");
				list.addItem("Sanitation");
				list.addItem("Food and Water Storage");
				list.addItem("Gym");
				list.addItem("Canteen");
				list.addItem("Power");
				list.addItem("Control");
				list.addItem("Airlock");
				list.addItem("Medical");
				
				//add the ListBox to panel1
				panel1.add(list, DockPanel.WEST);
				
				//create a button to close module list
				Button okButton = new Button("OK");
				okButton.addClickHandler(new ClickHandler() {
			        public void onClick(ClickEvent event) {
			        	modTypeText.setText( list.getItemText(list.getSelectedIndex()) );
			        	pop.hide();
			        }
				});
				
				//add panel1 and the button to the contents panel
				contents.add(panel1, DockPanel.NORTH);
				contents.add(okButton, DockPanel.NORTH);
				
				//add contents to pop panel
				pop.add(contents);		
				//show the pop panel
				pop.show();
			}});
	    
		//follow same procedure as before, creating a panel for each module
		//configuration
		modTypeText.setSize("300px", "50px");
		panel2.add(modTypeLabel, DockPanel.WEST);
		panel2.add(modTypeText, DockPanel.WEST);
		info.add(panel2, DockPanel.NORTH);
		
		//create the new panel and align it
		DockPanel panel3 = new DockPanel();
		panel3.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
		panel3.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);
		
		//create a label for a modules orientation
		Label modOrientLabel = new Label("Module Orientation");
		modOrientLabel.setSize("300px", "50px");
		final Label orientLabelText = new Label(String.valueOf(mod.getOrient()));
		orientLabelText.setSize("300px", "50px");
		orientLabelText.addClickHandler(new ClickHandler() {
	        public void onClick(ClickEvent event) {
				final PopupPanel pop = new PopupPanel();
				pop.setPopupPosition(302, 201);
				
				//create a new list for the different orientation options
				//0 = 0 turns needed
				//1 = 1 turn needed
				//2 = 2 turns needed
				DockPanel contents = new DockPanel();
				DockPanel panel1 = new DockPanel();
				panel1.add(new Label("Turns Needed"),DockPanel.WEST);
				final ListBox list = new ListBox();
				list.addItem("0");
				list.addItem("1");
				list.addItem("2");
				
				panel1.add(list, DockPanel.WEST);
				
				Button okButton = new Button("OK");
				okButton.addClickHandler(new ClickHandler() {
			        public void onClick(ClickEvent event) {
			        	orientLabelText.setText( list.getValue(list.getSelectedIndex()) );
			        	pop.hide();
			        }
				});
				
				contents.add(panel1, DockPanel.NORTH);
				contents.add(okButton, DockPanel.NORTH);
				pop.add(contents);				
				pop.show();
			}
	    });
		
		//add the orientation panel to the main module info panel
		panel3.add(modOrientLabel, DockPanel.WEST);
		panel3.add(orientLabelText, DockPanel.WEST);
		info.add(panel3, DockPanel.NORTH);
		
		//create a new panel for the modules location
		DockPanel panel4 = new DockPanel();
		panel4.setSize("640px", "50px");
		panel4.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
		panel4.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);
		Label locLabel = new Label("Module Location");
		locLabel.setSize("300px", "50px");
		final Label locText = new Label(String.valueOf(mod.getX()) + ", " + String.valueOf(mod.getY()));
		locText.setSize("300px", "50px");
		locText.addClickHandler(new ClickHandler() {
	        public void onClick(ClickEvent event) {
				final PopupPanel pop = new PopupPanel();
				pop.setPopupPosition(302, 201);
				
				//set text to the x and y locations
				DockPanel contents = new DockPanel();
				DockPanel panel1 = new DockPanel();
				panel1.add(new Label("X Location"),DockPanel.WEST);
				final TextBox textX = new TextBox();
				textX.setText(String.valueOf(mod.getX()));
				panel1.add(textX,DockPanel.WEST);
				
				DockPanel panel2 = new DockPanel();
				panel2.add(new Label("Y Location"),DockPanel.WEST);
				final TextBox textY = new TextBox();
				textY.setText(String.valueOf(mod.getY()));
				panel2.add(textY,DockPanel.WEST);
				
				//create "ok" button for location popup panel
				Button okButton = new Button("OK");
				okButton.addClickHandler(new ClickHandler() {
			        public void onClick(ClickEvent event) {
			        	locText.setText(textX.getText() + ", " + textY.getText());
			        	pop.hide();
			        }
				});
				
				//add the panels
				contents.add(panel1, DockPanel.NORTH);
				contents.add(panel2, DockPanel.NORTH);
				contents.add(okButton, DockPanel.NORTH);
				pop.add(contents);				
				pop.show();
			}
	    });
		
		panel4.add(locLabel, DockPanel.WEST);
		panel4.add(locText, DockPanel.WEST);
		info.add(panel4, DockPanel.NORTH);
		
		
		//create the pane for module status
		DockPanel panel5 = new DockPanel();
		panel5.setSize("640px", "50px");
		panel5.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
		panel5.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);
		Label statLabel = new Label("Module Status");
		statLabel.setSize("300px", "50px");
		final Label statusLabelText = new Label(mod.getStatus());
		statusLabelText.setSize("300px", "50px");
		statusLabelText.addClickHandler(new ClickHandler() {
	        public void onClick(ClickEvent event) {
				final PopupPanel pop = new PopupPanel();
				pop.setPopupPosition(302, 201);
				
				//create listBox for status options:
				//Undamaged
				//Damaged
				//Unknown
				DockPanel contents = new DockPanel();
				DockPanel panel1 = new DockPanel();
				panel1.add(new Label("Status"),DockPanel.WEST);
				final ListBox list = new ListBox();
				list.addItem("Undamaged");
				list.addItem("Damaged");
				list.addItem("Unknown");
				panel1.add(list, DockPanel.WEST);
				
				Button okButton = new Button("OK");
				okButton.addClickHandler(new ClickHandler() {
			        public void onClick(ClickEvent event) {
			        	statusLabelText.setText( list.getValue(list.getSelectedIndex()) );
			        	pop.hide();
			        }
				});
				
				contents.add(panel1, DockPanel.NORTH);
				contents.add(okButton, DockPanel.NORTH);
				pop.add(contents);				
				pop.show();
			}
	    });
		
		//create and add a Save and Close button option for module configuration
		panel5.add(statLabel, DockPanel.WEST);
		panel5.add(statusLabelText, DockPanel.WEST);
		info.add(panel5, DockPanel.NORTH);
		
		info.setVerticalAlignment(HasAlignment.ALIGN_BOTTOM);
		Button okButton = new Button("Save and Close");
		okButton.addClickHandler(new ClickHandler() {
	        public void onClick(ClickEvent event) {	        	
	        	if(mod != null)
	        	{
	        		
	        		Voices.playSavedModifications(); 
	        		//reset module stuff
		        	mod.setModType(modTypeText.getText());
		        	mod.setOrient(Integer.parseInt(orientLabelText.getText()));
		        	mod.setStatus(statusLabelText.getText().toString());
		        	
		        	//split the location text box into 2 strings, x and y
		        	String[] xy =  locText.getText().split(", ");
		        	
		        	//set x and y with those locations
		        	mod.setX(Integer.parseInt(xy[0]));
		        	mod.setY(Integer.parseInt(xy[1]));		        	     	
	        	}

				info.getParent().setVisible(false);
				mod.setConifigured();
			}
	    });
		
		//create and add a close and don't save button option for module configuration
		Button closeButton = new Button("Close without Saving");	
		closeButton.addClickHandler(new ClickHandler() {
	        public void onClick(ClickEvent event) {
				info.getParent().setVisible(false);
			}
	    });
		info.add(okButton, DockPanel.WEST);
		info.add(closeButton, DockPanel.WEST);
		
		return info;
	}	
}

